/*
 * @Author: sonss
 * @Date: 2023-07-04 17:00:16
 * @LastEditTime: 2024-08-23 11:10:54
 * @FilePath: /ss-file-antd/src/components/Uploader/Image.tsx
 * @Description:  图片上传控件
 */
import { Submit } from '@/services/upload';
import { PlusOutlined } from '@ant-design/icons';
import { GetProp, Image, Upload } from 'antd';
import { UploadChangeParam, UploadFile, UploadProps } from 'antd/es/upload/interface';
import { useEffect, useState } from 'react';
import { v4 as uuid } from 'uuid';

type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];

const getBase64 = (file: FileType): Promise<string> =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result as string);
    reader.onerror = (error) => reject(error);
  });

type ImageUploadProps = {
  multiple?: boolean;
  value?: string | string[];
  onChange?: (value: any) => void;
  onLoad?: (loading: boolean) => void;
  onError?: () => void;
};

const ImageUpload: React.FC<ImageUploadProps> = (props) => {
  const { multiple = false, value, onChange } = props;
  const [fileList, setFileList] = useState<UploadFile[]>([]);
  const [previewImage, setPreviewImage] = useState<string | undefined>();
  const [previewOpen, setPreviewOpen] = useState<boolean>(false);

  useEffect(() => {
    if (fileList.length > 0) {
      return;
    }

    let values: string[] = [];
    if (value) {
      if (value instanceof Array) {
        if (value.length > 0) {
          values = [...value];
        }
      } else {
        if (value) {
          values = [value];
        }
      }
    }

    const newList: UploadFile[] = values.map((url, index) => {
      return {
        uid: uuid(),
        name: 'image' + index,
        status: 'done',
        url,
        thumbUrl: url,
      };
    });

    setFileList(newList);
  }, [props]);

  const handleChange: UploadProps['onChange'] = (info: UploadChangeParam<UploadFile>) => {
    let newFileList = multiple
      ? [...info.fileList]
      : info.fileList.length > 0
      ? info.fileList.slice(-1)
      : [];

    if (info.file.status === 'uploading') {
      if (props.onLoad) {
        props.onLoad(true);
      }
    } else if (info.file.status === 'done') {
      const files = info.file?.response?.data?.files || [];
      const url = files.length > 0 ? files[0].url : undefined;
      
      info.file.url = url;
      const idx = newFileList.findIndex((v) => v.uid === info.file.uid);
      if (idx > -1) {
        newFileList[idx].url = url;
      }

      if (!multiple) {
        newFileList = [...newFileList.slice(-1)];
      }

      if (typeof onChange !== 'undefined') {
        if (multiple) {
          onChange(newFileList.map((v) => v.url));
        } else {
          onChange(newFileList[newFileList.length - 1].url);
        }
      }

      // 全部完成
      if (props.onLoad) {
        const okAll = newFileList.every((f) => f.status === 'done');
        if (okAll) {
          props.onLoad(false);
        }
      }
    } else if (info.file.status === 'error') {
      // info.file.url = undefined;
      // info.file.thumbUrl = undefined;

      const idx = newFileList.findIndex((v) => v.uid === info.file.uid);
      if (idx > -1) {
        newFileList[idx].url = undefined;
        newFileList[idx].thumbUrl = undefined;
      }

      if (props.onError) {
        props.onError();
      }
    }

    setFileList(newFileList);
  };

  const remove = async (file: UploadFile) => {
    const newFileList = fileList.filter((v) => v.uid !== file.uid);
    setFileList(newFileList);

    // 全部完成
    if (props.onLoad) {
      const okAll = newFileList.length > 0 ? newFileList.every((f) => f.status === 'done') : true;
      if (okAll) {
        props.onLoad(false);
      }
    }
  };

  const handlePreview = async (file: UploadFile) => {
    if (!file.url && !file.preview) {
      file.preview = await getBase64(file.originFileObj as FileType);
    }

    setPreviewImage(file.url || (file.preview as string));
    setPreviewOpen(true);
  };

  const handleCustomRequest = async (config: any) => {
    const options = {
      onUploadProgress: (ev: any) => {
        const p = Math.round((ev.loaded * 100) / ev.total!);
        config.onProgress(p);
      },
    };

    try {
      const formData = new FormData();
      formData.append('files[]', config.file);

      Submit(formData, options).then((res) => {
        if (res.code !== 0) {
          config.onError(res.message || '上传失败');
          return;
        }
        config.file.status = 'done';
        config.file.url = res.data?.url;
        config.onSuccess(res);
      });
    } catch (e) {
      config.onError(e);
    }
  };

  return (
    <>
      <Upload
        listType="picture-card"
        multiple={multiple}
        maxCount={9}
        accept="image/*"
        fileList={fileList}
        customRequest={handleCustomRequest}
        onChange={handleChange}
        onRemove={remove}
        onPreview={handlePreview}
      >
        <div>
          <PlusOutlined />
          <br />
          <p>单击上传</p>
        </div>
      </Upload>

      {previewImage && (
        <Image
          wrapperStyle={{ display: 'none' }}
          preview={{
            visible: previewOpen,
            onVisibleChange: (visible) => setPreviewOpen(visible),
            afterOpenChange: (visible) => !visible && setPreviewImage(undefined),
          }}
          src={previewImage}
        />
      )}
    </>
  );
};
export default ImageUpload;
